#ifndef __tenacitas_communication__switchboard__h__
#define __tenacitas_communication__switchboard__h__



/** @file */


/** @todo */

// ==> C++ Headers
#include <thread>
#include <utility>

// ==> 3rds Headers

// ==> Our Headers
#include <tenacitas.log/text_file.h>

// ==> Namespaces
using namespace std;

// ==> Macro Constants

// ==> Macro Commands


/** ==> namespace of the organization */
namespace tenacitas {
    /** ==> namespace for the project */
    namespace communication {

        // ==> Typedefs 

        // ==> Pre-Declarations

        // ==> Attributes 

        // ==> Functions
        
        // ==> Classes

        /**

           @tparam t_receiver should implement:
           @code
           bool start();

           t_channel && wait_for_call();
           @endcode

           @tparam t_handler should implement:
           @code
           bool operator()(t_channel &&);
           @endcode

           @nosubgrouping 
        */
        template <typename t_receiver,
                  typename t_channel,
                  typename t_handler,
                  typename t_logger>
        class switchboard {

            // ==> switchboard - Friends

            /** Class allowed to access private members of @p switchboard, in order
                to perform tests */
            friend class switchboard_tester;

        public:

            // ==> switchboard - Public Internal Types

            /** */
            typedef t_receiver receiver;

            /** */
            typedef t_channel channel;

            /** */
            typedef t_handler handler;

            /** */
            typedef t_logger logger;


            // ==> switchboard - Public Constructors

            /** */
            switchboard(receiver && p_receiver,
                        handler && p_handler)
                : m_stop(true),
                  m_receive_thread() {
                m_receiver = std::move(p_receiver);
                m_handler = std::move(p_handler);
            }
            
            /** */
            switchboard() = delete;

            /***/
            switchboard(const switchboard &) = delete;

            /***/
            switchboard(switchboard &&) = delete;
            
            
            /** Destructor */
            ~switchboard( ) {
                m_receiver.stop();
            }

            // ==> switchboard - Public Accessors

            // ==> switchboard - Public Helpers

            // ==> switchboard - Public Processors

            /** */
            void start_sync() {

                if ( !m_stop ) {
                    return;
                    // @todo: log warn that the receiver was already started
                }

                m_stop = false;
                
                start();
            }
            

            /** */
            void start_async() {
                if ( !m_stop ) {
                    return;
                    // @todo: log warn that the receiver was already started
                }
                m_stop = false;
                
                m_receive_thread = std::thread( switchboard::start, *this );
            }

            /** */
            void stop() {
                m_stop = true;
            }
            

            // ==> switchboard - Public Operators

            /***/
            switchboard & operator == (const switchboard &) = delete;

            /***/
            switchboard & operator == (switchboard &&) = delete;


            // ==> switchboard - Public Attributes

        private:

            // ==> switchboard - Private Internal Types

            // ==> switchboard - Private Constructors

            // ==> switchboard - Private Accessors

            // ==> switchboard - Private Helpers

            // ==> switchboard - Private Processors

            /** */
            void start() noexcept {

                typename logger::ptr l_log( logger::create( ) );

                // starts the receiving engine
                if ( !m_receiver.start( ) ) {
                    // @todo log critical; throw exception
                    log_error(l_log) << "Error starting connector";
                    return;
                }

                // we will receive calls, using the defined concurrence policy,
                // until someone tells us to stop 
                while ( !m_stop ) {
                    log_info(l_log) << "Waiting for a call...";

                    // wait to receive a call
                    channel l_channel = std::move(m_receiver.wait_for_call());

                    // handler the call
                    if (!m_handler(std::move(l_channel))) {
                        log_warn(l_log) << "Unable to handle call";
                        m_stop = true;
                    }
                }
            }
            
            // ==> switchboard - Private Attributes

            /** */
            bool m_stop;

            /** Thread where the @p receiver will start receiving calls */
            std::thread m_receive_thread;

            /** */
            receiver m_receiver;

            /** */
            handler m_handler;
      
        };
    }
}

// =============================================================================
// I M P L E M E N T A T I O N
// =============================================================================

using namespace tenacitas::communication;


#endif
